package com.coupons.business.common.context.manager;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Map;
import java.util.logging.Logger;

import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.access.BeanFactoryLocator;
import org.springframework.beans.factory.access.SingletonBeanFactoryLocator;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class CouponCommonAppContextManager {
	

	
	private String beanFactoryConfigXmlFileName = null;
	private String defaultBeanFactoryName = null;

	/*
	 * can override this method example: in order to construct spring context from difference spring xml other than the default 
	 * 	or to support multiple deployments
	 *  along with this method override the getImplementingClass method
	 */	
	protected static Class getImplementingClass()
	{
		return CouponCommonAppContextManager.class;
	}
		
	/**
	 * Default Constructor having spring file and factory name
	 * @throws Exception
	 */
	protected CouponCommonAppContextManager() throws Exception
	{
		this("spring-config.xml", "CouponBeanComponents");
	}
	
	/**
	 * Constructor having spring file and factory name
	 * @param _beanFactoryConfigXmlFileName
	 * @param _defaultBeanFactoryName
	 * @throws Exception
	 */
	protected CouponCommonAppContextManager(String _beanFactoryConfigXmlFileName, String _defaultBeanFactoryName) throws Exception
	{
		beanFactoryConfigXmlFileName = _beanFactoryConfigXmlFileName;
		defaultBeanFactoryName = _defaultBeanFactoryName;
		loadSpringBeanFactory(beanFactoryConfigXmlFileName, defaultBeanFactoryName);
		singleton = this;
	}
	
	
	protected static CouponCommonAppContextManager singleton = null;

	//private static Logger logger = Logger.getLogger(CouponCommonAppContextManager.class);

	protected ClassPathXmlApplicationContext defaultBeanFactory;
	
	protected BeanFactoryLocator beanFactoryLocator = null;

	/**
	 * Loading spring file and factory name
	 * @param beanFactoryConfigXmlFileName
	 * @param defaultBeanFactoryName
	 * @throws Exception
	 */
	protected synchronized void loadSpringBeanFactory(String beanFactoryConfigXmlFileName, String defaultBeanFactoryName) throws Exception
	{

		try {
			//logger.debug("Starting loading of bean configuration="+beanFactoryConfigXmlFileName);
			beanFactoryLocator = SingletonBeanFactoryLocator
					.getInstance(beanFactoryConfigXmlFileName);
			defaultBeanFactory = (ClassPathXmlApplicationContext) beanFactoryLocator
					.useBeanFactory(defaultBeanFactoryName).getFactory();
			//logger.debug("Completed successfully loading of beans="+beanFactoryConfigXmlFileName);
		} catch (Exception e) {
			
			
			//logger.error("Error Loading beanFactoryConfigXmlFile="+beanFactoryConfigXmlFileName+",beanFactoryName="+defaultBeanFactoryName+"-"
				//	+ stackTraceString(e));
			throw e;
		}

	}
	
	
	public static CouponCommonAppContextManager getInstance()
	{
		if ( singleton == null || singleton.getDefaultBeanFactory() == null )
		{
			try {
				setSingletonInstance(getNewInstance());
			} catch (Exception e) {
				singleton=null;
				e.printStackTrace();
				//logger.error(e.getMessage(),e);
				throw new RuntimeException("The Spring Factory could not be loaded",e);
			}
		}
		return singleton;
	}
	
	private static synchronized void setSingletonInstance(CouponCommonAppContextManager instance)
	{
		if ( singleton == null || singleton.getDefaultBeanFactory() == null )
		{
			singleton = instance;
		}
	}
	
	/*
	 * can override this method example: in order to construct spring context from difference spring xml other than the default 
	 * 	or to support multiple deployments
	 *  along with this method override the getImplementingClass method
	 */
	protected static synchronized CouponCommonAppContextManager getNewInstance() throws Exception
	{
		return new CouponCommonAppContextManager();
	}
	
	private synchronized void reloadSpringBeanFactory()
	{
		if ( defaultBeanFactory == null || !defaultBeanFactory.isActive() )
		{
//			logger.error(defaultBeanFactoryName, new RuntimeException("The Spring Factory,"+defaultBeanFactoryName+
//												 " got closed and trying to re-open again from "+beanFactoryConfigXmlFileName));
			try {
				loadSpringBeanFactory(beanFactoryConfigXmlFileName, defaultBeanFactoryName);
			} catch (Exception e) {
				e.printStackTrace();
				//logger.error(e.getMessage(), e);
				throw new RuntimeException("The Spring Factory,"+defaultBeanFactoryName+" some body closed and could not re-open again from "+beanFactoryConfigXmlFileName,e);
			}
		}
	}
	
	/**
	 * 
	 * @param name
	 * @return
	 */
	public Object getBeanByName(String name) {
		return getInstance().getDefaultBeanFactory().getBean(name);
	}
	
	/**
	 * 
	 * @param name
	 * @return
	 */
	public boolean isContainsBeanByName(String name) {
		return getInstance().getDefaultBeanFactory().containsBean(name);
	}
	
	/**
	 * 
	 * @param classType
	 * @return
	 */
	public Map getBeansOfClassType(Class classType) {
		return BeanFactoryUtils.beansOfTypeIncludingAncestors(getInstance().getDefaultBeanFactory(),
				classType, true, true);
	}
	
	public void destroy()
	{	
		if ( defaultBeanFactory != null )
		{
			try
			{
				defaultBeanFactory.close();
			}
			catch(Exception ex)
			{				
			}
		}
		//logger.debug("Destroy of BeanUtil is completed");
		
		defaultBeanFactory = null;
		beanFactoryLocator = null;
	}
			
	/* (non-Javadoc)
	 * @see java.lang.Object#finalize()
	 */
	protected void finalize() throws Throwable {
		super.finalize();
		if ( defaultBeanFactory != null )
		{
			//logger.debug("finalize is called, Please verify if this is legal", new RuntimeException("Spring Factory,"+defaultBeanFactoryName+" from " + beanFactoryConfigXmlFileName + " was loaded."));
		}
	}

	/**
	 * Returns string representation of Stack trace of exception. Useful in log
	 * statements. Ex : log.debug(Helper.stackTraceString(e));
	 * 
	 * @param t
	 *            Exception
	 * @return String Exception
	 */
	public static String stackTraceString(Throwable t) {

		StringWriter stringWriter = new StringWriter();
		PrintWriter printWriter = new PrintWriter(stringWriter);
		t.printStackTrace(printWriter);

		StringBuffer error = stringWriter.getBuffer();

		return error.toString();

	}

	
	/**
	 *  @deprecated
	**/
	public static ClassPathXmlApplicationContext getBeanFactory()
	{
		return getInstance().getDefaultBeanFactory();
	}
	
	public ClassPathXmlApplicationContext getDefaultBeanFactory()
	{
		if ( defaultBeanFactory == null || !defaultBeanFactory.isActive() )
		{
			reloadSpringBeanFactory();
		}
		return defaultBeanFactory;
	}
	

	/**
	 * Returns the beans of given class type
	 *
	 *	@deprecated
	 * 
	 * @param classType
	 *            the classType for which beans are to be retrieved
	 * @return
	 */
	public static Map getBeansOfType(Class classType) {
		return BeanFactoryUtils.beansOfTypeIncludingAncestors(getInstance().getDefaultBeanFactory(),
				classType, true, true);
	}

	/**
	 * This method is used to check whether the given bean is declared and
	 * whether it is present in the classpath
	 * 
	 *	@deprecated
	 *
	 * @param name
	 *            the name of the bean which should be checked for existance
	 * @return true if the bean with the given name exists and false otherwise
	 */
	public static boolean containsBean(String name) {
		return getInstance().getDefaultBeanFactory().containsBean(name);
	}

	/**
	 * This method returs the given bean instance
	 * 
	 * @deprecated
	 * 
	 * @param name
	 *            the name of the bean which should be returned
	 * @return the instance of the given bean
	 */
	public static Object getBean(String name) {
		return getInstance().getDefaultBeanFactory().getBean(name);
	}

}

